// --- Helper methods for int arrays ---

/*  Allocates memory for a 2-d array
 */
void allocate(int ***array, int rows, int cols){
	*array = new int* [rows]; // pointers to the rows
	(*array)[0] = new int [rows*cols]; // actual data block
	for (int i=1; i<rows; i++)
		(*array)[i] = (*array)[i-1] + cols; // point the row-pointers to appropriate data block locations
}

/*  Allocates memory for a 3-d array1
 */
void allocate(int ****array, int dim1, int dim2, int dim3){
	*array = new int** [dim1]; // pointer to the first dimension
	for (int i=0; i<dim1; i++)
		allocate(&((*array)[i]), dim2, dim3);
}

/*  Allocates memory for a 1-d array
 */
void allocate(int **array, int length){
	*array = new int[length]; // allocate data block
}

/*  Frees up memory for a 2-d array
 */
void deallocate(int ***array){
	delete [] (*array)[0]; // free up block of ints
	delete [] (*array); // free up row pointers
}

/*  Frees up memory for a 3-d array
 */
void deallocate(int ****array, int dim1){
	for (int i=0; i<dim1; i++)
		deallocate(&((*array)[i])); // free up each 2-d array
	delete [] (*array); // free up row pointers
}

/*  Frees up memory for a 1-d array
 */
void deallocate(int **array){
	delete [] (*array); // free up block of ints
}

/*  Prints a 1-d array to standard output
 */
void print(int *array, int length){
	for (int i=0; i<length; i++)
		cout << array[i] << " " ;
	cout << endl;
}

/*  Prints a 2-d array to standard output
 */
void print(int **array, int rows, int cols){
	for (int i=0; i<rows; i++){
		for (int j=0; j<cols; j++)
			cout << array[i][j] << " " ;
		cout << endl;
	}
}

/*  Prints transpose of a 2-d array to standard output
 */
void printTranspose(int **array, int rows, int cols){
	for (int i=0; i<cols; i++){
		for (int j=0; j<rows; j++)
			cout << array[j][i] << " " ;
		cout << endl;
	}
}

/*  Transposes a 2-d array
 */
void transpose(int **destination, int **source, int sourceRows, int sourceCols){
	for (int i=0; i<sourceRows; i++)
		for (int j=0; j<sourceCols; j++)
			destination[j][i] = source[i][j];
}

/*  Sum up a 1-d array
 */
int sum(int *array, int length){
	int answer=0;
	for (int i=0; i<length; i++)
		answer += array[i];
	return answer;
}

/* Sum up a 2-d array
 */
int sum(int *result, int **array, int rows, int cols, int direction){
	if (direction == ROW) {
		for (int i=0; i<rows; i++){
			result[i] = 0;
			for (int j=0; j<cols; j++)
				result[i] += array[i][j];
		}
		return (int)(log(0)); // this return should not be used
	} else if (direction == COL) {
		for (int j=0; j<cols; j++)
			result[j] = 0;
		for (int i=0; i<rows; i++)
			for (int j=0; j<cols; j++)
				result[j] += array[i][j];
		return (int)(log(0)); // this return should not be used
	} else if (direction == ALL) {
		int answer=0;
		for (int i=0; i<rows; i++)
			for (int j=0; j<cols; j++)
				answer += array[i][j];
		return answer;
	}
	return ERROR;
}

/*  Scale up a 1-d array by a constant
 */
void scale(int z, int *array, int length){
	for (int i=0; i<length; i++)
		array[i] *= z;
}

/*  Scale up a 2-d array by a constant
 */
void scale(int z, int **array, int rows, int cols){
	for (int i=0; i<rows; i++)
		for (int j=0; j<cols; j++)
			array[i][j] *= z;
}

/*  Scale up a 2-d array by a 1-d array
 */
void scale(int *z, int **array, int rows, int cols, int direction){
	if (direction == ROW) {
		for (int i=0; i<rows; i++)
			for (int j=0; j<cols; j++)
				array[i][j] *= z[i];
	} else if (direction == COL) {
		for (int i=0; i<rows; i++)
			for (int j=0; j<cols; j++)
				array[i][j] *= z[j];
	}
}


/*  Add to a 1-d array
 */
void add(int z, int *array, int length){
	for (int i=0; i<length; i++)
		array[i] += z;
}

/*  Add to a 2-d array
 */
void add(int z, int **array, int rows, int cols){
	for (int i=0; i<rows; i++)
		for (int j=0; j<cols; j++)
			array[i][j] += z;
}

/*  Element-by-element Multiplication of two 1-d arrays
 */
void mult(int *result, int *array1, int *array2, int length){
	for (int i=0; i<length; i++)
		result[i] = array1[i]*array2[i];
}

/*  Element-by-element Multiplication of two 2-d arrays
 */
void mult(int **result, int **array1, int **array2, int rows, int cols){
	for (int i=0; i<rows; i++)
		for (int j=0; j<cols; j++)
			result[i][j] = array1[i][j] * array2[i][j];
}

/*  Dot product of two 1-d arrays
 */
int dotProduct(int* array1, int* array2, int length){
	int answer = 0;
	for (int i=0; i<length; i++)
		answer += array1[i]*array2[i];
	return answer;
}

/*  Copy for 2-d arrays
 */
void copy(int **destination, int **source, int rows, int cols){
	for (int i=0; i<rows; i++)
		for (int j=0; j<cols; j++)
			destination[i][j] = source[i][j];
}

/*  Copy for 1-d arrays
 */
void copy(int *destination, int *source, int length){
	for (int i=0; i<length; i++)
		destination[i] = source[i];
}

/*  Set to a value for 1-d arrays
 */
void setValue(int z, int *array, int length){
	for (int i=0; i<length; i++)
		array[i] = z;
}

/*  Set to a value for 2-d arrays
 */
void setValue(int z, int **array, int rows, int cols){
	for (int i=0; i<rows; i++)
		for (int j=0; j<cols; j++)
			array[i][j] = z;
}

/*  Horizontal concatenation for 2-d arrays
 */
void horzcat(int **result, int rows, int **array1, int cols1, int **array2, int cols2){
	for (int i=0; i<rows; i++){
		for (int j=0; j<cols1; j++)
			result[i][j] = array1[i][j];
		for (int j=0; j<cols2; j++)
			result[i][cols1+j] = array2[i][j];
	}
}

/*  Vertical concatenation for 2-d arrays
 */
void vertcat(int **result, int cols, int **array1, int rows1, int **array2, int rows2){
	for (int i=0; i<rows1; i++)
		copy(result[i], array1[i], cols);
	for (int i=0; i<rows2; i++)
		copy(result[rows1+i], array2[i], cols);
}

/*  Prints a 2-d array to file
 */
void print(int **array, int rows, int cols, char* outFileName){
	ofstream outfile;
	outfile.open(outFileName);
	for (int i=0; i<rows; i++){
		for (int j=0; j<cols; j++)
			outfile << array[i][j] << " " ;
		outfile << endl;
	}
	outfile.close();
}

/*  Prints a 1-d array to file
 */
void print(int *array, int length, char* outFileName){
	ofstream outfile;
	outfile.open(outFileName);
	for (int i=0; i<length; i++)
		outfile << array[i] << " " ;
	outfile << endl;
	outfile.close();
}

/*  Computes mean over a 2-d array
 */
void mean(double* result, int start, int end, int** array, int rows, int cols){
	setValue(0, result, cols);
	for (int i=start; i<=end; i++)
		for (int j=0; j<cols; j++)
			result[j] += array[i][j];
	scale(1.0/(end-start+1), result, cols);
	
}

/*  Computes mean over a 3-d array
 */
void mean(double** result, int start, int end, int*** array, int dim1, int dim2, int dim3){
	setValue(0, result, dim2, dim3);
	for (int i=start; i<=end; i++)
		for (int j=0; j<dim2; j++)
			for (int l=0; l<dim3; l++)
				result[j][l] += array[i][j][l];
	scale(1.0/(end-start+1), result, dim2, dim3);
}
